#ifndef ENGINE_KINETIC_MOVEMENT_H
#define ENGINE_KINETIC_MOVEMENT_H


#include "../Synaptic/Misc.h"

namespace Engine {

namespace Kinetic {

//Interpolates movement. Plans to add multiple destinations.
class Movement {
	public: /* TODO: set private */
		Misc::Vector2d departure; // Where its coming from
		Misc::Vector2d position; // Where we are now
		Misc::Vector2d destination; // Where its going to
		Misc::Vector2d velocity; // Leaded by
		Misc::Vector2d breakPoint; // When to break
		double maxSpeed; 
		double speed; 
		double acceleration;
		double friction;
		
		double distanceToCruise;
		double distanceToBreak;
		double totalDistance;
		double halfDistance;
		
		bool smooth;
		bool targeted;

	public:
		/* Set up targeted and interpolated movement */
		Movement(const Misc::Vector2d& from, const Misc::Vector2d& to, double accel, double friction, double speed, double max); 
		
		/* Set up kinetic movement */
		Movement(const Misc::Vector2d & from, double accel, double friction, double maxSpeed);

		Movement(); 
		virtual ~Movement(); 
		
		virtual Misc::Vector2d update(int delta, bool accelerating = true);
		
		inline virtual Misc::Vector2d readLocation() { return position; };
		inline virtual Misc::Vector2d readVelocity() { return velocity; };
		inline virtual double readSpeed() { return speed; };
		
		inline virtual Movement & setDestination(const Misc::Vector2d & newDestination, bool smooth = true);
		inline virtual Movement & setDeparture(const Misc::Vector2d & newDeparture) { position = departure = newDeparture; return *this; };
		inline virtual Movement & setSpeed(double newSpeed) { speed = newSpeed; return *this; };
		inline virtual Movement & setMaxSpeed(double newMaxSpeed) { maxSpeed = newMaxSpeed; return *this; } 
		inline virtual Movement & setAccel(double newAcceleration) { acceleration = newAcceleration; return *this; }
		inline virtual Movement & setFriction(double newFriction) { friction = newFriction; return *this; }
		inline virtual Movement & setVelocity(Misc::Vector2d & newVelocity) { velocity = newVelocity; return *this; };
		
};

inline Movement & Movement::setDestination(const Misc::Vector2d & newDestination, bool smooth) { 
	destination = newDestination;
	targeted = true;
	this->smooth = smooth;
	if (smooth) {
		departure = position;
		// lovely torricelli
		distanceToCruise = (maxSpeed*maxSpeed - speed*speed)/(2.0f*acceleration);
		totalDistance = departure.distance(destination);
		distanceToBreak = (totalDistance - distanceToCruise);
		halfDistance = totalDistance/2.0f;
		breakPoint = destination*((totalDistance - distanceToCruise)/totalDistance);
	}
	velocity = (destination - position).normalize();
	return *this;
};

} // namespace Engine::Kinetic

} // namespace Engine
#endif
